package org.zn.note.jdk.net;

import java.io.*;
import java.net.Socket;

/**
 * @ClassName JavaIO
 * @Author zn
 * @Date 2019/4/12 16:32
 **/
public class JavaIO {
    /*
    IO，不能狭义的理解为网络通信，而是数据传输。可能是网络，也可能是文件、管道、内存缓存等等。
    流，可以理解为一种媒介或封装，连接两端的。“流”中的数据是比特的，字节流、字符流是封装。
    另外，每多少个数据怎么解析（字符流是依赖字符集的字节流封装）等、有没有缓存，都是上面的封装和实现了。
    比如网络通信，可能的数据流过程：
        网卡 --> 网卡驱动(保证数据正确) --> 系统级封装(比如缓存与调度) --> 应用程序内存(提供“流”操作的句柄)
    如果是从文件读呢：
        硬盘 --> 硬盘驱动 -- > 操作系统(文件系统) --> 应用程序内存(提供“流”操作的句柄)
    Java提供的IO可以理解为 “流”的操作句柄的相关API，具体数据怎么从操作系统中来是JVM应该实现的。
    所以这个数据怎么来（比如Socket连接、数据传输、复制等）不是JavaIO关心的，
    在操作JavaIO相关API时，其实不要关心数据来源，而应该关心数据操作本身，要这样理解。
    至于后面的 NIO、AIO 的优化，可能是操作系统层面的优化，JVM仅仅是调用；也可能是JVM本身的优化。
    这个要研究的。
     */
    /*
    JavaIO 提供 字节流[InputStream/OutputStream]、字符流[Reader/Writer]
    直接接收(发送)数据的有（节点流）：
            文件：FileInputStream、FileOutputStream、FileReader、FileWriter
            数组：ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
            管道：PipedInputStream、PipedOutputStream、PipedReader、PipedWriter
            字符串：StringReader、StringWriter
    其他的都是对于上面的封装，可以理解为上面这些是下面这些的“数据源”：
            缓冲流：BufferedInputStream、BufferedOutputStream 、BufferedReader、BufferedWriter
            转换流：InputStreamReader、OutputStreamWriter
            对象流：ObjectInputStream、ObjectOutputStream
            抽象基类：FilterInputStream、FilterOutputStream、FilterReader、FilterWriter
            打印流 ：PrintStream、PrintWriter
            推回输入流：PushbackInputStream、PushbackReader
            特殊流：DataInputStream、DataOutputStream
     */

    // Socket 流实例：SocketInputStream/SocketOutputStream
    // 分别继承自 FileInputStream/FileOutputStream，当做文件处理
    private static void testSocketStream() {
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            socket = new Socket("localhost", 60000);
            in = socket.getInputStream();
            out = socket.getOutputStream();
            out.write("zn".getBytes());
            int a = 1;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 由于不关心数据来源，只关心怎么操作数据，所以选用String为来源
    // 这些API也只是为了让我们操作数据方便一些
    private static void testStringReader() {
        String s = "123456789";
        // 只关心 Reader API本身，不关心数据来源是StringReader还是FileReader
        // StringReader的方法内几乎都有同步处理，是线程安全的
        Reader reader = new StringReader(s);
        int readValue = -1;
        try {
            // block、0~65535 [0x00-0xffff]、end -1
            while(-1 != (readValue = reader.read())) {
                System.out.print((char)readValue + " ");
            }
            System.out.println();
            // 相当于位置的指针重新指向第一个字符
            reader.reset();
            // 重置后，重新获取到字符'1'
            System.out.println((char)reader.read());
            // 判断当前Reader是否支持mark方法
            if(reader.markSupported()) {
                // 如果支持mark方法，当reset的时候，跳到mark
                reader.mark(3);
                reader.reset();
                System.out.println((char)reader.read());
            } else {
                System.out.println("当前Reader实现不支持mark");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // JDK 1.7 增加了 AutoCloseable，可以自动关闭，要这样写
        try(Reader reader2 = new StringReader(s);) {
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void testStringWriter() {
        // 其实内部是 new StringBuffer
        Writer writer = new StringWriter();
        try {
            writer.write("12345");
            // StringWriter.flush 不做任何事
            writer.flush();
            System.out.println(((StringWriter) writer).getBuffer().toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(writer);
        }
    }

    private static void testFileIo() {
        String filePath = "d:" + File.separator + "a.txt";
        File file = new File(filePath);
        InputStream in = null;
        OutputStream out = null;
        Reader reader = null;
        Writer writer = null;
        try {
            if(!file.exists()) {
                file.createNewFile();
            }
            // new的时候直接open了
            in = new FileInputStream(file);
            out = new FileOutputStream(file);
            reader = new FileReader(file);
            writer = new FileWriter(file);
            out.write("abcd".getBytes());
            // FileInputStream.flush 没用，没实现，空的
            out.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(in);
            close(out);
            close(reader);
            close(writer);
        }
    }

    private static void testPiped() {
        final PipedInputStream in = new PipedInputStream();
        final PipedOutputStream out = new PipedOutputStream();
        try {
            out.connect(in);
            out.write("1234abcd".getBytes());
            out.write("1234abcd".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(out);
            close(in);
        }
    }

    public static void main(String[] args) {
        testPiped();
    }

    private static void close(Closeable closeable) {
        if(null != closeable) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
